import 'package:backend.core/core/domain/entities/google_play/google_play_one_time_purchased_item_transaction.dart';
import 'package:backend.core/core/domain/entities/google_play/google_play_purchased_subscription_transaction.dart';
import 'package:backend.core/core/domain/entities/google_play/google_play_purchased_subscription_transaction_offer_type.dart';
import 'package:backend.core/core/domain/entities/google_play/google_play_transaction.dart';
import 'package:backend.core/core/domain/entities/google_play/google_play_transaction_revocation_info.dart';
import 'package:backend.core/core/domain/entities/google_play/google_play_transaction_revocation_reason.dart';
import 'package:backend.core/core/domain/entities/google_play/google_play_transaction_revocation_source.dart';
import 'package:backend.core/core/domain/repositories/google_play_transaction_repository/repository.dart';
import 'package:built_collection/built_collection.dart';
import 'package:equatable/equatable.dart';
import 'package:mongo_dart/mongo_dart.dart';

part 'dtos/one_time_purchased_item_transaction_dto.dart';

part 'dtos/purchased_item_transaction_dto.dart';

part 'dtos/purchased_item_transaction_dto_factory.dart';

part 'dtos/purchased_subscription_offer_type_dto.dart';

part 'dtos/purchased_subscription_transaction_dto.dart';

part 'dtos/time_dto.dart';

part 'dtos/transaction_revocation_info_dto.dart';

part 'dtos/transaction_revocation_reason_dto.dart';

part 'dtos/transaction_revocation_source_dto.dart';

const _kCollectionKey = 'google_play_purchased_item_transactions';

class GooglePlayTransactionRepositoryImpl implements GooglePlayTransactionRepository {
  const GooglePlayTransactionRepositoryImpl({
    required Db db,
  }) : _db = db;

  final Db _db;

  @override
  Future<GooglePlayTransaction?> findTransactionByOrderId({
    required String orderId,
  }) async {
    final collection = _db.collection(_kCollectionKey);

    final data = await collection.findOne(
      where.eq(_kPurchasedItemTransactionOrderIdKey, orderId),
    );

    if (data == null) {
      return null;
    }

    final purchasedItemTransaction = _PurchasedItemTransactionDtoFactory.fromJson(data).toEntity();

    return purchasedItemTransaction;
  }

  @override
  Future<BuiltList<GooglePlayTransaction>> findUserTransactions({
    required String googlePlayUserId,
  }) async {
    final collection = _db.collection(_kCollectionKey);

    final purchasedItemTransactionStream = collection.find(
      where.eq(_kPurchasedItemTransactionGooglePlayUserIdKey, googlePlayUserId),
    );

    final purchasedItemTransactionJsons = await purchasedItemTransactionStream.toList();

    final purchasedItemTransactions =
        purchasedItemTransactionJsons.map((purchasedItemTransactionJson) {
      return _PurchasedItemTransactionDtoFactory.fromJson(purchasedItemTransactionJson).toEntity();
    }).toBuiltList();

    return purchasedItemTransactions;
  }

  @override
  Future<BuiltList<GooglePlayTransaction>> findAllTransactions() async {
    final collection = _db.collection(_kCollectionKey);

    final purchasedItemTransactionStream = collection.find();
    final purchasedItemTransactionJsons = await purchasedItemTransactionStream.toList();

    final purchasedItemTransactions =
        purchasedItemTransactionJsons.map((purchasedItemTransactionJson) {
      return _PurchasedItemTransactionDtoFactory.fromJson(purchasedItemTransactionJson).toEntity();
    }).toBuiltList();

    return purchasedItemTransactions;
  }

  @override
  Future<void> upsertTransaction({
    required GooglePlayTransaction purchasedItemTransaction,
  }) async {
    final collection = _db.collection(_kCollectionKey);

    final purchasedItemTransactionJson =
        _PurchasedItemTransactionDtoFactory.fromEntity(purchasedItemTransaction).toJson();

    await collection.replaceOne(
      where.eq(_kPurchasedItemTransactionOrderIdKey, purchasedItemTransaction.orderId),
      purchasedItemTransactionJson,
      upsert: true,
    );
  }

  @override
  Future<void> upsertTransactions({
    required BuiltList<GooglePlayTransaction> purchasedItemTransactions,
  }) async {
    if (purchasedItemTransactions.isEmpty) {
      return;
    }

    final collection = _db.collection(_kCollectionKey);

    final bulk = UnorderedBulk(collection);

    for (final purchasedItemTransaction in purchasedItemTransactions) {
      final purchasedItemTransactionJson =
          _PurchasedItemTransactionDtoFactory.fromEntity(purchasedItemTransaction).toJson();

      bulk.replaceOne(ReplaceOneStatement(
        where
            .eq(_kPurchasedItemTransactionOrderIdKey, purchasedItemTransaction.orderId)
            .map[key$Query],
        purchasedItemTransactionJson,
        upsert: true,
      ));
    }

    await bulk.executeDocument();
  }
}
